##########################################
## Copyright 2012-2013 Ceruti Francesco & contributors
##
## This file is part of LiSP (Linux Show Player).
##########################################

import gi
gi.require_version('Gst', '1.0')
import sys
import lisp.plugin as plugin
import lisp.layout as layout

from PyQt4 import QtGui, QtCore
from gi.repository import GObject, Gst

from lisp.ui.mainwindow import MainWindow
from lisp.ui.layoutselect import LayoutSelect
from lisp.ui import styles
from lisp.core.collector import Collector
from lisp.core.media import Media
from lisp._xml import xml_reader, xml_writer
from lisp.utils.progress import Progress
from lisp.utils import configuration as cfg
from lisp.midi import midi


class Main:

    def __init__(self):
        QtGui.QIcon.setThemeSearchPaths(styles.QLiSPIconsThemePaths)
        QtGui.QIcon.setThemeName(styles.QLiSPIconsThemeName)

        self.mainWindow = MainWindow()
        self.collector = Collector()
        self.collectorUi = None
        self.programConf = {}
        try:
            midi.initialize()
            self.midiHandler = midi.MidiEventHandler(int(cfg.config['MIDI']['DeviceID']))
            self.midiHandler.start()
        except:
            self.midiHandler = None
            QtGui.QMessageBox.critical(self.mainWindow, 'Error', 'MIDI event handler cannot be started!')

        self.mainWindow.newProgramAction.connect(self.newProgram)
        self.mainWindow.saveProgramAction.connect(self.saveProgram)
        self.mainWindow.openProgramAction.connect(self.openProgram)
        self.mainWindow.addMediaAction.connect(self.addMedia, QtCore.Qt.QueuedConnection)

        if(cfg.config['Layout']['UseDefault'] == 'True'):
            self.createLayout(cfg.config['Layout']['Default'])
        else:
            self.mainWindow.setEnabled(False)
            while(not self.layoutDialog()):
                pass
            self.mainWindow.setEnabled(True)

        try:
            self.plugins = plugin.initPlugins(self)
        except Exception as e:
            QtGui.QMessageBox.critical(None, 'Error Message', ' '.join([str(i) for i in e.args]))

    def layoutDialog(self):
        select = LayoutSelect()
        select.exec_()
        if(select.result() == QtGui.QDialog.Accepted):
            self.createLayout(select.getSlected())
            return True
        else:
            return False

    def createLayout(self, lay):
        self.mainWindow.menuLayout.clear()

        if(self.collectorUi is not None):
            self.collectorUi.destroyLayout()
            self.mainWindow.multiEditAction.disconnect()
            self.mainWindow.selectAllAction.disconnect()
            self.mainWindow.deselectAllAction.disconnect()
            self.mainWindow.invertSelectionAction.disconnect()

        self.collectorUi = layout.buildLayout(lay, self, parent=self.mainWindow.centralwidget)

        self.mainWindow.multiEditAction.connect(self.collectorUi.editSelectedMedia)
        self.mainWindow.selectAllAction.connect(self.collectorUi.selectAll)
        self.mainWindow.deselectAllAction.connect(self.collectorUi.deselectAll)
        self.mainWindow.invertSelectionAction.connect(self.collectorUi.invertSelection)

        self.mainWindow.setProgramLayout(self.collectorUi)
        self.programConf['layout'] = lay

    def addMedia(self, files):
        try:
            for file in files:

                conf = {}
                conf['location'] = file
                conf['name'] = file.split('/')[len(file.split('/')) - 1]

                media = self.collectorUi.getMediaClass()(conf)

                self.collectorUi.addMedia(media)
        except Exception as e:
            QtGui.QMessageBox.critical(None, 'Error Message', ' '.join([str(i) for i in e.args]))

    def playByPos(self, pos):
        media = self.collectorUi.mediaAt(pos)
        if(media is not None):
            media.play()

    def stopByPos(self, pos):
        media = self.collectorUi.mediaAt(pos)
        if(media is not None):
            media.stop()

    def pauseByPos(self, pos):
        media = self.collectorUi.mediaAt(pos)
        if(media is not None):
            media.pause()

    def seekByPos(self, pos, time_ms):
        media = self.collectorUi.mediaAt(pos)
        if(media is not None):
            media.seek(time_ms)

    def newProgram(self):
        self.collectorUi.destroyLayout()
        self.collector.resetCollector()
        self.collectorUi = None
        self.mainWindow.file = ''
        while(not self.layoutDialog()):
            pass
        self.resetPlugin()

    def saveProgram(self, filepath):
        writer = xml_writer.XmlWriter(filepath)
        for media in self.collectorUi.mediaList():
            if(media is not None):
                if(media.state != Media.NONE):
                    writer.appendMedia(media.conf, media.uuid)

        writer.appendProgramSettings(self.programConf)

        for plug in self.plugins:
            data = self.getPluginData(plug)
            if(data is not None):
                writer.appendPluginSettings(plug.PLUGIN_NAME, data)

        writer.writeFile()

    def openProgram(self, filepath):
        progress = Progress(title='Loading ' + filepath)
        progress.show()
        try:
            progress.setLabelText('Reading file ...')
            reader = xml_reader.XmlReader(filepath)
            conf = reader.readMediaList()
            progress.setMaximum(len(conf) + 10)
            pluginsConf = reader.readPluginsSettings()
            self.programConf = reader.readProgramSettings()

            progress.setLabelText('Creating layout ...')
            self.createLayout(self.programConf['layout'])
            self.mainWindow.update()
            progress.setValue(progress.value() + 5)

            progress.setLabelText('Loading media ...')
            pfunc = lambda w: progress.setValue(progress.value() + 1)
            self.collector.onMediaAdded.connect(pfunc)
            self.collectorUi.readProgramConf(conf)
            self.collector.onMediaAdded.disconnect(pfunc)

            progress.setLabelText('Loading plugins ...')
            self.resetPlugin()
            self.reloadPlugins()
            self.setPluginsData(pluginsConf)
            progress.hide()
        except Exception as e:
            progress.hide()
            QtGui.QMessageBox.critical(None, 'Error Message', 'Error during file reading: ' + ' '.join([str(i) for i in e.args]))
            self.newProgram()

    def setPluginsData(self, conf, debug=False):
        for plug in self.plugins:
            if(plug.PLUGIN_NAME in conf):
                if(debug):
                    plug.setData(conf[plug.PLUGIN_NAME])
                else:
                    try:
                        plug.setData(conf[plug.PLUGIN_NAME])
                    except:
                        raise Exception('Plugin data read failed: ' + plug.PLUGIN_NAME)

    def reloadPlugins(self, debug=False):
        for plug in self.plugins:
            if(debug):
                plug.reload()
            else:
                try:
                    plug.reload()
                except:
                    raise Exception('Plugin load failed: ' + plug.PLUGIN_NAME)

    def resetPlugin(self, debug=False):
        for plug in self.plugins:
            if(debug):
                plug.reset()
            else:
                try:
                    plug.reset()
                except:
                    raise Exception('Plugin reset failed: ' + plug.PLUGIN_NAME)

    def getPluginData(self, plugin, debug=False):
        if(debug):
            return plugin.getData()
        else:
            try:
                return plugin.getData()
            except:
                raise Exception('Plugin data save failed: ' + plugin.PLUGIN_NAME)


def exec_(app):
    app.exec_()

    # Terminate pypm (portmidi)
    midi.terminate()


def main():
    app = QtGui.QApplication(sys.argv)
    app.setApplicationName('Linux Show Player (beta version)')
    app.setQuitOnLastWindowClosed(True)
    # Force light font, for environment with bad QT support.
    appFont = app.font()
    appFont.setWeight(QtGui.QFont.Light)
    app.setFont(appFont)

    if(cfg.config['Themes']['UseStandAloneTheme'] == 'True'):
        app.setStyleSheet(styles.QLiSPTheme_Dark)

    appMain = Main()  # @UnusedVariable

    GObject.threads_init()
    Gst.init(None)

    sys.exit(exec_(app))

if __name__ == "__main__":
    main()
